package com.watson.minio.springminio.file.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.watson.minio.springminio.file.dao.FileManageMapper;
import com.watson.minio.springminio.file.entity.FileManage;
import com.watson.minio.springminio.file.service.FileManageService;
import com.watson.minio.springminio.file.util.Base64DecodedMultipartFile;
import com.watson.minio.springminio.file.util.FileUtil;
import com.watson.minio.springminio.file.util.IdGeneratorSnowflake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;

import javax.annotation.Resource;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static com.watson.minio.springminio.file.util.FileTypeUtil.*;


/**
 * @Description: 文件管理类
 * @Author: fan
 * @Data: 2021/5/8 9:59
 * @Version: 1.0v
 */
@Service
public class FileManageImpl extends ServiceImpl<FileManageMapper, FileManage> implements FileManageService {

    private static final Logger log = LoggerFactory.getLogger(FileManageImpl.class);

    /**
     * java判断文件是否相同的的方式
     * 1：计算文件的 MD5 值 根据MD5值 判断文件是否是同一个文件
     * 2：计算文件的 SHA-1 值 根据SHA-1值 判断文件是否是同一个文件
     * 3：直接判断内容是否相同  读取文件后，将其转换成字节流：
     * 4： 校验base64 位字符串形式的文件是否相等
     * 5：判断是不是同一个网络路径
     */

    @Value("${com.cpiinfo.iot.file.commonPath}")
    private String fileCommonPath;

    @Resource
    private IdGeneratorSnowflake idGeneratorSnowflake;


    @Override
    public String fileUpload(MultipartFile file, String module) {

        log.info("file->{}", file);
        //判断为空
        if (ObjectUtil.isEmpty(file)) {
            throw new RuntimeException("文件不存在");
        }
        try {
            //操作文件 包括上传文件，存储数据库等
            File tempFile = FileUtil.multipartFileToFile(file, fileCommonPath);
            return doFile(tempFile, module);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文件上传失败->e:{},{}", e.getMessage(), e);
            return "文件上传失败";
        }

    }

    @Override
    public String fileUpload(File file, String module) {
        log.info("file->{}", file);
        //判断为空
        try {
            //操作文件 包括上传文件，存储数据库等
            return doFile(file, module);
        } catch (Exception e) {
            log.error("文件上传失败->e:{},{}", e.getMessage(), e);
            return "文件上传失败";
        }
    }

    /**
     * 通过网络路径上传文件
     *
     * @param url 文件网格路径
     * @return 访问路径
     */
    @Override
    public String fileUpload(String url, String module) {
        if (ObjectUtil.isEmpty(url)) {
            return "文件路径不能为空";
        }

        //不是以https 开头的
        if (!url.startsWith("http")) {
            return "网络路径必须以http开始";
        }

        // 进来第一步判断有不有相同的网络路径
        FileManage fileManage = getOne(Wrappers.lambdaQuery(FileManage.class).eq(FileManage::getSrc, url).last("limit 1"));
        if (ObjectUtil.isNotEmpty(fileManage)) {
            // 图片访问路径
            return fileManage.getAccessPath();
        }

        try {
            //建立远程连接
            HttpURLConnection httpUrlConnection = doConnection(url);
            BufferedInputStream bin = new BufferedInputStream(httpUrlConnection.getInputStream());

            //取文件后缀名
            String fileSufName = getSuffixFromUrl(url);
            if (ObjectUtil.isEmpty(fileSufName)) {
                fileSufName = getFileSufName(new URL(url).openStream());
            }
            if (ObjectUtil.isEmpty(fileSufName)) {
                fileSufName = getMIMETypeFromUrl(url);
            }
            if (ObjectUtil.isEmpty(fileSufName)) {
                fileSufName = getMIMETypeFromInputStream(bin);
            }
            if (ObjectUtil.isEmpty(fileSufName)) {
                fileSufName = url.substring(url.lastIndexOf(".") + 1);
            }

            if (ObjectUtil.isEmpty(fileSufName)) {
                log.error("文件上传失败,文件名为空");
                return "文件上传失败";
            }
            //网络文件名不方便取也不重要，用路径拼接后缀代替文件名
            //filePath: 自定义文件存位置
            String fileName = idGeneratorSnowflake.snowflakeId() + "." + fileSufName;

            //创建文件
            File file = cn.hutool.core.io.FileUtil.touch(new File(fileName));
            file = cn.hutool.core.io.FileUtil.writeFromStream(bin, file);

            return doFile(file, module);
        } catch (Exception e) {
            throw new RuntimeException("文件上传失败");
        }
    }

    @Override
    public String fileUploadByBase64(String base64, String module) throws IOException {

        String[] baseStrs = base64.split(",");
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] b;
        b = decoder.decodeBuffer(baseStrs[1]);
        for (int i = 0; i < b.length; ++i) {
            if (b[i] < 0) {
                b[i] += 256;
            }
        }
        Base64DecodedMultipartFile base64DecodedMultipartFile = new Base64DecodedMultipartFile(b, baseStrs[0]);
        return fileUpload(base64DecodedMultipartFile, module);
    }

    @Override
    public List<String> fileUploadBatch(MultipartFile[] files, String module) {

        List<String> urlList = new ArrayList<>();
        //判断为空
        if (ObjectUtil.isEmpty(files)) {
            throw new RuntimeException("文件不存在");
        }
        try {
            for (MultipartFile file : files) {
                //判断为空
                if (file.isEmpty()) {
                    throw new IOException("文件不存在");
                }
                String path = doFile(FileUtil.multipartFileToFile(file, fileCommonPath), module);

                urlList.add(path);
            }

            return urlList;
        } catch (Exception e) {
            throw new RuntimeException("文件上传失败");
        }
    }

    /**
     * @param contextPath 网格路径
     * @return 真实地址
     * @throws Exception 连接失败
     */
    private HttpURLConnection doConnection(String contextPath) throws Exception {
        // 统一资源
        URL url = new URL(contextPath);
        // 连接类的父类，抽象类
        URLConnection urlConnection = url.openConnection();
        // http的连接类
        HttpURLConnection httpUrlConnection = (HttpURLConnection) urlConnection;
        //设置超时
        httpUrlConnection.setConnectTimeout(1000 * 5);
        //设置请求方式，默认是GET
        httpUrlConnection.setRequestMethod("GET");
        // 设置字符编码
        httpUrlConnection.setRequestProperty("Charset", "UTF-8");

        return httpUrlConnection;
    }

    private String getFileSufName(InputStream is) throws IOException {
        byte[] b = new byte[3];
        is.read(b, 0, b.length);
        //头文件
        String headCode = FileUtil.bytesToHexString(b);
        headCode = headCode.toUpperCase();
        log.info("头文件代码:" + headCode);
        String sufName = FileUtil.checkType(headCode);
        log.info("文件后缀:" + sufName);

        return sufName;
    }

    /**
     * 文件流上传
     *
     * @param file 文件
     * @return 文件访问地址
     */
    private String doFile(File file, String module) {
        if (ObjectUtil.isEmpty(file)) {
            throw new RuntimeException("文件不存在");
        }

        //判断是否有相同文件已经上传 把文件转成Md5
        String md5 = SecureUtil.md5(file);
        // 数据库有相同的文件 云查的数据库
        String path = validFileIsExist(md5);
        if (!ObjectUtil.isEmpty(path)) {
            return path;
        }

        //重命名文件
        String fileName = file.getName();
        log.info("fileName->{}", fileName);
        int index = fileName.lastIndexOf(".");
        if (index == -1) {
            // 根据文件流的头部信息获得文件类型
            fileName += "." + cn.hutool.core.io.FileUtil.getType(file);
        }
        index = fileName.lastIndexOf(".");
        if (index == -1) {
            throw new RuntimeException("上传文件类型有误");
        }
        String newFileName = idGeneratorSnowflake.snowflakeId() + fileName.substring(index);

        log.info("newFileName->{}", newFileName);
        //上传文件
        Integer integer = booleanToInteger(FileUtil.getImg(file));

        String imgUrl = fileCommonPath + File.separator + module + File.separator + DateUtil.format(DateUtil.date(), "YYYY-MM-dd");
        File fileDirectory = new File(imgUrl);
        if (!fileDirectory.exists()) {
            fileDirectory.mkdirs();
        }
        // 修改文件名
        String resultImageUrl = fileDirectory.getAbsolutePath() + File.separator + newFileName;

        File copyFile = new File(resultImageUrl);

        cn.hutool.core.io.FileUtil.copyContent(file, copyFile, false);

        log.info("resultImageUrl->{}", resultImageUrl);

//        SysUser sysUser = SecurityUtil.getSysUser();

        //将上传成功的文件存入数据库
        FileManage fileManage = new FileManage();
        fileManage.setName(fileName);
        fileManage.setUploadName(newFileName);
        fileManage.setType(cn.hutool.core.io.FileUtil.getType(file));
        fileManage.setModule(module);
        fileManage.setSize(cn.hutool.core.io.FileUtil.size(file));
        fileManage.setSuffix(fileName.substring(fileName.lastIndexOf(".")));
        fileManage.setMd5(md5);
        fileManage.setAccessPath(resultImageUrl);
        fileManage.setIsPicture(integer);
        fileManage.setCreateAt(new Date());
        fileManage.setCreateBy("sysUser.getUserCode()");
        fileManage.setCreateByName("sysUser.getUserName()");

        log.info("存入数据库的实例 fileManage->{}", fileManage);
        save(fileManage);

        cn.hutool.core.io.FileUtil.del(file);

        return resultImageUrl;

    }

    /**
     * 通过文件的md5 判断是否有相同的文件
     *
     * @param md5 文件md5内容
     * @return 有就返回访问路径，没有返回null
     */
    private String validFileIsExist(String md5) {
        log.info("md5->{}", md5);

        FileManage manage = getOne(new LambdaQueryWrapper<>(FileManage.class).eq(FileManage::getMd5, md5).last("limit 1"));

        log.info("md5查询相同数据->{}", md5);
        if (ObjectUtil.isEmpty(manage)) {
            return null;
        }
        // 图片访问路径
        return manage.getAccessPath();
    }

    /**
     * 将布尔值转换为整型
     *
     * @param bl Boolean类型
     * @return true返回1 false返回0
     */
    private Integer booleanToInteger(boolean bl) {
        if (bl) {
            return 1;
        }

        return 0;
    }


    public static void main(String[] args) throws IOException, InterruptedException {

        FileManageImpl fileManage = new FileManageImpl();
        File file = new File("D:\\data\\file\\commonPath\\111.png");
        fileManage.doFile(file, "1111");

        Thread.sleep(200000);

    }

}
